﻿using AutojsController.Config;
using AutojsController.Helpers;
using AutojsController.Services;
using Avalonia.Logging;
using Avalonia.Media.Imaging;
using Avalonia.Platform.Storage;
using ReactiveUI;
using Serilog;
using SukiUI.Toasts;
using System;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.IO;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Threading.Tasks;
using System.Windows.Input;

namespace AutojsController.ViewModels
{
    public class ScriptDataModel:INotifyPropertyChanged
    {
        private Script _script;
        public Script Script
        {
            get => _script;
            set {
                _script = value;
                OnPropertyChanged(nameof(Script));
                OnPropertyChanged(nameof(Icon));
                OnPropertyChanged(nameof(StatusIcon));
            }
        }
        public Bitmap? Icon
        {
            get => Image.LoadFromResource(this.Script.Type switch
            {
                ScriptType.Project => "/Assets/Icons/ProjectScript.png",
                _ => "/Assets/Icons/SingleScript.png"
            });
        }

        public Bitmap? StatusIcon
        {
            get => Image.LoadFromResource(this.Script.Validated ? "/Assets/Icons/validated.png" : "/Assets/Icons/unvalidated.png");
        }

        private bool isSelected = false;
        public bool IsSelected
        {
            get => isSelected;
            set
            {
                if (isSelected != value)
                {
                    isSelected = value;
                    OnPropertyChanged();
                }
            }
        }

        public string StatusText
        {
            get => this.Script.Validated ? "已验证" : "未验证";
        }

        public ScriptDataModel(Script script) { this._script = script; }

        public event PropertyChangedEventHandler? PropertyChanged;
        protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = "")
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }
    }
    public class ScriptViewModel : ViewModelBase
    {
        private readonly ISukiToastManager toastManager;
        private readonly ScriptsService srpService;

        public ObservableCollection<ScriptDataModel> Scripts { get; set; } = new ();
        public ICommand AssignToGroupCommand => ReactiveCommand.CreateFromTask<int>(assginSelectedScriptToGroupAsync);
        public ICommand ScanCommand => ReactiveCommand.CreateFromTask(scanScripts);
        public ICommand ImportCommand => ReactiveCommand.CreateFromTask<IStorageProvider>(importScriptAsync);
        public ICommand ValidateCommand => ReactiveCommand.CreateFromTask<int>(validateScriptAsync);
        public ICommand DeleteCommand => ReactiveCommand.CreateFromTask(deleteScriptAsync);
        private readonly ILogger logger = LoggerFactory.GetLogger();

        public ScriptViewModel(ISukiToastManager toast, ScriptsService srpservice) : base("脚本管理", Material.Icons.MaterialIconKind.ScriptText, 10)
        {
            this.toastManager = toast;
            this.srpService = srpservice;
            freshScriptsAsync().Wait();
        }

        private async Task freshScriptsAsync()
        {
            var srps = await srpService.GetAllScriptAsync();
            Scripts = new ObservableCollection<ScriptDataModel>(srps.Select(s => new ScriptDataModel(s)));
            this.RaisePropertyChanged(nameof(Scripts));
        }

        public void SelectScriptOrNot(ScriptDataModel script)
        {
            if (script.IsSelected)
                script.IsSelected = false;
            else
            {
                SetScriptSelected(script);
            }
        }

        public void SetScriptSelected(ScriptDataModel script)
        {
            foreach (var srp in Scripts) srp.IsSelected = false;
            script.IsSelected = true;
        }

        private async Task assginSelectedScriptToGroupAsync(int grpId)
        {
            var selectedSrp = Scripts.FirstOrDefault(s => s.IsSelected);
            if (selectedSrp == null) return;
            await srpService.AssignScriptToGroupAsync(selectedSrp.Script.Id, grpId);
            logger.Information($"将脚本{selectedSrp.Script.Name}分配给组ID:{grpId}");
        }

        private async Task scanScripts()
        {
            var dir = Path.Combine(AppContext.BaseDirectory, Consts.SCRIPT_DIRECTORY_NAME);
            if (!Directory.Exists(dir))
            {
                Directory.CreateDirectory(dir);
                return;
            }
            logger.Information("开始扫描脚本目录");
            foreach (var file in Directory.GetFiles(dir))
            {
                try
                {
                    var scriptName = Path.GetFileNameWithoutExtension(file);
                    switch (Path.GetExtension(file))
                    {
                        case ".js":
                            await srpService.CreateScriptAsync(Path.Combine(Consts.SCRIPT_DIRECTORY_NAME, file),
                                scriptName, ScriptType.Single);
                            logger.Information($"添加单个脚本{scriptName}");
                            break;
                        case ".zip":
                            await srpService.CreateScriptAsync(Path.Combine(Consts.SCRIPT_DIRECTORY_NAME, file),
                                scriptName, ScriptType.Project);
                            logger.Information($"添加项目脚本{scriptName}");
                            break;
                        default:
                            break;
                    }
                }
                catch
                {
                    continue;
                }
            }
            await freshScriptsAsync();
        }

        private async Task importScriptAsync(IStorageProvider provider)
        {
            if (provider == null)
            {
                SukiToast.Error(toastManager, "无法打开文件选择框", 3);
                return;
            }
            var files = await provider.OpenFilePickerAsync(new FilePickerOpenOptions
            {
                AllowMultiple = false,
                FileTypeFilter = new[]
                {
                    new FilePickerFileType("文件")
                    {
                        Patterns = new[] { "*.js" },
                        MimeTypes = new[] { "application/javascript" }
                    },
                    new FilePickerFileType("项目")
                    {
                        Patterns = new[] { "*.zip" },
                        MimeTypes = new[] { "application/zip" }
                    }
                },
                Title = "选择文件(js)或者项目(zip)"
            });
            var filePath = files.FirstOrDefault()?.Path.LocalPath;
            if (filePath == null || !File.Exists(filePath)) { return; }
            if (await srpService.IsExistScriptNameAsync(Path.GetFileNameWithoutExtension(filePath)))
            {
                SukiToast.Error(toastManager, "已存在该脚本,请更换另一个文件名", 3);
                return;
            }
            if (new FileInfo(filePath).Length > Consts.SCRIPT_MAX_BYTES)
            {
                SukiToast.Error(toastManager, $"脚本超过大小限制{Consts.SCRIPT_MAX_BYTES / 1024 / 1024}M", 3);
                return;
            }
            var localPath = Path.Combine(AppContext.BaseDirectory, Consts.SCRIPT_DIRECTORY_NAME, Path.GetFileName(filePath));
            await FileHelper.CopyAsync(filePath, localPath);
            var scriptName = Path.GetFileNameWithoutExtension(filePath);
            await srpService.CreateScriptAsync(Path.Combine(Consts.SCRIPT_DIRECTORY_NAME, Path.GetFileName(filePath)),
                                               scriptName, Path.GetExtension(filePath) == "*.js" ? ScriptType.Single : ScriptType.Project);
            logger.Information(string.Format("添加{0}脚本{1}", Path.GetExtension(filePath) == "*.js" ? "单个" : "项目", scriptName));
            await freshScriptsAsync();
        }

        private async Task validateScriptAsync(int mobId)
        {
            var selectedSrp = Scripts.FirstOrDefault(s => s.IsSelected);
            if (selectedSrp == null) return;
            //TODO: 使用MobileConnectionService发送脚本
            await Task.Delay(1000);
            logger.Information($"验证脚本{selectedSrp.Script.Name}成功");
            var srp = await srpService.SetScriptValidationAsync(selectedSrp.Script.Id);
            if (srp != null)
                selectedSrp.Script = srp;
        }

        /// <summary>
        /// 仅删除数据库不移除脚本，如果想恢复重新扫描即可
        /// </summary>
        /// <returns></returns>
        private async Task deleteScriptAsync()
        {
            var selectedSrp = Scripts.FirstOrDefault(s => s.IsSelected);
            if (selectedSrp == null) return;
            await srpService.DeleteScriptAsync(selectedSrp.Script.Id);
            logger.Information($"删除脚本{selectedSrp.Script.Name}");
            Scripts.Remove(selectedSrp);
            await freshScriptsAsync();
        }
    }
}
